1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 package org.webmacro.directive;
24
25 import org.webmacro.directive.Directive.ArgDescriptor;
26 import org.webmacro.directive.Directive.Subdirective;
27
28 /***
29 * Each directive needs a DirectiveDescriptor to describe how it
30 * should be parsed and built by the parser. The directive descriptor
31 * identifies the directive's name, the class of the underlying concrete
32 * directive object, a list of directive argument descriptors, and a list
33 * of subdirective descriptors.
34 *
35 * If the directive does not specify the class, the directive provider will
36 * fill it in automatically.
37 *
38 * The args field is an array of Directive.ArgDescriptor objects. There
39 * are static nested classes within Directive for each type of argument
40 * -- Condition, LValue, RValue, Keyword, Punctuation, Block, LiteralBlock,
41 * and special argument descriptors for OptionalGroup and OptionalChoice.
42 * These allow the directive writer to specify
43 * a flexible syntax for directive arguments.
44 * Each directive can have a set of subdirectives, and each subdirective
45 * can have its own argument list. Subdirectives can be required, optional,
46 * or optional-repeating (multiple subdirectives of the same kind, like
47 * #elseif.) There are constructors for Subdirective in the Directive module
48 * as well.
49 * @author Brian Goetz
50 */
51
52 public final class DirectiveDescriptor
53 {
54
55 public String name;
56 public Class dirClass;
57 public ArgDescriptor[] args;
58 public Subdirective[] subdirectives;
59
60 public boolean valid = false,
61 hasBreakingSubdirectives = false;
62
63 public DirectiveDescriptor (String name,
64 Class dirClass,
65 ArgDescriptor[] args,
66 Subdirective[] subdirectives)
67 {
68 this.name = name;
69 this.dirClass = dirClass;
70 this.args = args;
71 this.subdirectives = subdirectives;
72
73 completeArgs(this.args);
74 valid = validateArgs(this.args);
75
76 if (subdirectives != null)
77 {
78 for (int i = 0; i < this.subdirectives.length; i++)
79 {
80 completeArgs(this.subdirectives[i].args);
81 valid &= validateArgs(this.subdirectives[i].args);
82 if (this.subdirectives[i].isBreaking)
83 hasBreakingSubdirectives = true;
84 }
85 }
86 }
87
88 /***
89 * Determines the index of the next argument following a given argument.
90 * May return an index > args.length.
91 */
92 private static int nextArg (ArgDescriptor[] args, int i)
93 {
94 if (args[i].type == Directive.ArgType_GROUP
95 || args[i].type == Directive.ArgType_CHOICE)
96 {
97 int k = i + 1;
98 for (int j = 0; j < args[i].subordinateArgs; j++)
99 k = nextArg(args, k);
100 return k;
101 }
102 else
103 return i + 1;
104 }
105
106 /***
107 * Set the nextArg, children[] fields as necessary
108 */
109 private static void completeArgs (ArgDescriptor[] args)
110 {
111 int j, k;
112
113 for (int i = 0; i < args.length; i++)
114 args[i].nextArg = nextArg(args, i);
115
116 for (int i = 0; i < args.length; i++)
117 {
118 switch (args[i].type)
119 {
120 case Directive.ArgType_GROUP:
121 case Directive.ArgType_CHOICE:
122 args[i].children = new int[args[i].subordinateArgs];
123 for (j = 0, k = i + 1; j < args[i].subordinateArgs; j++)
124 {
125 args[i].children[j] = k;
126 k = args[k].nextArg;
127 }
128 break;
129
130 default:
131 break;
132 }
133 }
134 }
135
136 /***
137 * Make sure that the structure of the arguments list is valid.
138 * This means that
139 * GROUP arguments begin with a keyword, not optional
140 * Each of the children of a CHOICE argument is an OPTIONAL GROUP
141 */
142 private static boolean validateArgs (ArgDescriptor[] args)
143 {
144 boolean valid = true;
145 for (int i = 0; i < args.length; i++)
146 {
147 if (args[i].type == Directive.ArgType_GROUP)
148 {
149 if (args[i].subordinateArgs == 0)
150 valid = false;
151 else if ((args[args[i].children[0]].type != Directive.ArgType_KEYWORD
152 && args[args[i].children[0]].type != Directive.ArgType_ASSIGN)
153 || args[args[i].children[0]].optional)
154 valid = false;
155 }
156 else if (args[i].type == Directive.ArgType_CHOICE)
157 {
158 for (int j = 0; j < args[i].subordinateArgs; j++)
159 {
160 if (args[args[i].children[j]].type != Directive.ArgType_GROUP
161 || !args[args[i].children[j]].optional)
162 valid = false;
163 }
164 }
165 }
166
167 return valid;
168 }
169 }